﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vowei.Data.Converters.Excel;
using Vowei.Core.Models;
using NPOI.HSSF.UserModel;
using System.IO;
using NPOI.SS.UserModel;
using Vowei.Data.Models;
using System.Linq.Expressions;
using System.Globalization;
using Vowei.Core;

namespace Vowei.Data.Converters.Excel
{
    public class DataConnectionWorkItemExcelConverter : ExcelConverter<IEnumerable<ITable>>
    {
        enum ItemType
        {
            Requirement,
            Task
        }

        private ITicketStatusManager _statusManager;
        public DataConnectionWorkItemExcelConverter(ITicketStatusManager statusManager) 
            : this(statusManager, "", DefaultTranslator)
        {
        }

        public DataConnectionWorkItemExcelConverter(ITicketStatusManager statusManager, string sheetName)
            : this(statusManager, sheetName, DefaultTranslator)
        {
        }

        public DataConnectionWorkItemExcelConverter(ITicketStatusManager statusManager, string sheetName, ITranslator translator)
            : base(translator)
        {
            WorkSheetName = sheetName;
            _statusManager = statusManager;
        }
        
        public override bool CanConvert(Stream stream)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            if (Translator == null)
                throw new InvalidOperationException(DefaultTranslator.T("在从Excel导入数据之前，请指定用户转换日期、数字、货币等格式的区域设置！"));

            var memoryStream = new MemoryStream((int)stream.Length);
            stream.CopyTo(memoryStream);
            var workbook = new HSSFWorkbook(memoryStream);
            var sheet = GetWorkItemsSheet(workbook);
            var firstRow = GuessFirstRow(sheet, FieldName(m => m.ID), false);
            return firstRow >= 0;
        }

        public override IEnumerable<ITable> ConvertFrom(System.IO.Stream stream)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            if (Translator == null)
                throw new InvalidOperationException(DefaultTranslator.T("在从Excel导入数据之前，请指定用户转换日期、数字、货币等格式的区域设置！"));

            var memoryStream = new MemoryStream((int)stream.Length);
            stream.CopyTo(memoryStream);
            var workbook = new HSSFWorkbook(memoryStream);
            var sheet = GetWorkItemsSheet(workbook);
            var firstRow = GuessFirstRow(sheet, FieldName(m => m.ID));

            // TODO: 下面这些列名需要跟前台Index.cshtml网页上的列名完全一致才能导入
            // 硬编码将会导致项目维护困难，而且在实现国际化支持的时候也会很麻烦
            // 因此建议在实现国际化的时候，同时解决这个问题。
            var map = ReadColumnNames(sheet.GetRow(firstRow), new string[] {
                FieldName(m => m.ID), // "Id",
                FieldName(m => m.Title), // "名称",
                FieldName(m => m.AssignedTo), // "负责人",
                Translator.T<Task, DateTime?>(m => m.StartDate), // "起始时间",
                Translator.T<Task, DateTime?>(m => m.DueDate), // "期望结束时间",
                "类型",
                "Type",
                Translator.T<Requirement, float>(m => m.BusinessValue), // "商业价值",
                FieldName(m => m.BelongsToMilestone) // "隶属里程碑"
            },
            0, 256);

            // TODO: 这个应该要修改成国际化的方式
            if ((map["类型"] != -1) && map["Type"] != -1)
                throw new InvalidDataException(string.Format(Translator.T("在试图导入的Excel文件的{0}工作表里，不能同时包含‘类型’和‘Type’列！"), WorkSheetName));
            if ((map["类型"] == -1) && map["Type"] == -1)
                throw new InvalidDataException(string.Format(Translator.T("在试图导入的Excel文件的{0}工作表里，需要包含‘类型’和‘Type’其中的一列！"), WorkSheetName));

            var result = new List<ITable>();
            for (int i = (firstRow + 1); i < sheet.LastRowNum + 1; ++i)
            {
                var row = sheet.GetRow(i);

                try
                {
                    var id = ReadCell<string>(row, map[FieldName(m => m.ID)], false);
                    var type = ReadType(row, map);
                    ITable item = null;

                    switch (type)
                    {
                        case ItemType.Requirement:
                            item = ReadRequirementFrom(row, map);
                            break;

                        case ItemType.Task:
                            item = ReadTaskFrom(row, map);
                            break;

                        default:
                            break;
                    }

                    if (!string.IsNullOrEmpty(id))
                        item.ID = Guid.Parse(id);

                    result.Add(item);
                }
                catch (Exception e)
                {
                    throw new InvalidDataException(string.Format(Translator.T("在试图导入Excel文件的“{0}”工作表失败，第{1}行有误, 具体错误信息是：{2}"), WorkSheetName, i + 1, e.Message), e);
                }
            }

            return result;
        }

        private Requirement ReadRequirementFrom(Row row, Dictionary<string, int> map)
        {
            var req = new Requirement()
            {
                Title = ReadCell<string>(row, map[FieldName(r => r.Title)]),
                AssignedTo = ReadCell<string>(row, map[FieldName(r => r.AssignedTo)], false),
                BusinessValue = ReadCell<int>(row, map[Translator.T<Requirement, float>(r => r.BusinessValue)], false),
            };

            var mid = ReadGuidCell(row, map[FieldName(r => r.BelongsToMilestone)]);
            if (mid.HasValue)
                req.BelongsToMilestoneId = mid.Value;

            if (req.Progress == 1.0f)
                _statusManager.Close(req);

            return req;
        }

        private Task ReadTaskFrom(Row row, Dictionary<string, int> map)
        {
            var task = new Task()
            {
                Title = ReadCell<string>(row, map[FieldName(t => t.Title)]),
                StartDate = ReadDateCell(row, map[Translator.T<Task, DateTime?>(t => t.StartDate)]),
                DueDate = ReadDateCell(row, map[Translator.T<Task, DateTime?>(t => t.DueDate)]),
                AssignedTo = ReadCell<string>(row, map[FieldName(t => t.AssignedTo)], false)
            };

            var mid = ReadGuidCell(row, map[FieldName(t => t.BelongsToMilestone)]);
            if (mid.HasValue)
                task.BelongsToMilestoneId = mid.Value;

            if (task.Progress == 1.0f && task.DueDate.HasValue)
            {
                _statusManager.Close(task, task.DueDate.Value);
            }

            return task;
        }

        private ItemType ReadType(Row row, Dictionary<string, int> map)
        {
            var result = ItemType.Task;
            string typeRaw = null;
            if (map.ContainsKey("类型"))
                typeRaw = ReadCell<string>(row, map["类型"], true);
            else if (map.ContainsKey("Type"))
                typeRaw = ReadCell<string>(row, map["Type"], true);

            if (string.Compare(typeRaw, "Requirement") == 0)
                result = ItemType.Requirement;
            else if (string.Compare(typeRaw, "需求") == 0)
                result = ItemType.Requirement;

            return result;
        }

        public override void ConvertTo(IEnumerable<ITable> users, System.IO.Stream stream)
        {
            throw new NotSupportedException(Translator.T("DataConnectionWorkItemExcelConverter实例只能用来读取使用Excel数据连接的Excel文件，不能创建这样的文件！"));
        }
    }
}
